/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.springblade.modules.admin.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.AllArgsConstructor;
import org.springblade.common.cache.DictCache;
import org.springblade.core.boot.ctrl.BladeController;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.mp.support.Condition;
import org.springblade.core.mp.support.Query;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tenant.annotation.TenantIgnore;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.Func;
import org.springblade.modules.admin.entity.MaintainMalfunctionWork;
import org.springblade.modules.admin.entity.MaintainPlanContent;
import org.springblade.modules.admin.entity.MaintainPlanContentExt;
import org.springblade.modules.admin.entity.MaintainPlanRelation;
import org.springblade.modules.admin.enums.MaintainPlanContentEnum;
import org.springblade.modules.admin.enums.TaskStatusEnum;
import org.springblade.modules.admin.service.IMaintainMalfunctionWorkService;
import org.springblade.modules.admin.service.IMaintainPlanContentExtService;
import org.springblade.modules.admin.service.IMaintainPlanContentService;
import org.springblade.modules.admin.service.IMaintainPlanRelationService;
import org.springblade.modules.admin.vo.MaintainMalfunctionWorkVO;
import org.springblade.modules.admin.vo.MalFunctionDetailVO;
import org.springblade.modules.admin.vo.TaskStatusVO;
import org.springblade.modules.admin.vo.elevator.ProblemItemVo;
import org.springblade.modules.admin.vo.parts.MalfunctionPartsTreeVO;
import org.springblade.modules.admin.vo.plan.MalfunctionWorkSummaryVo;
import org.springblade.modules.admin.vo.plan.wx.BiWxPlanDetailVo;
import org.springblade.modules.admin.vo.plan.wx.bi.BiWxUserPlanVo;
import org.springblade.modules.admin.wrapper.MaintainMalfunctionWorkWrapper;
import org.springblade.modules.system.entity.Dict;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 故障工单明细 控制器
 *
 * @author zcc
 * @since 2021-05-11
 */
@RestController
@AllArgsConstructor
@RequestMapping("admin/maintain/malfunction/work")
@Api(value = "故障工单明细", tags = "故障工单明细接口")
public class MaintainMalfunctionWorkController extends BladeController {

	private final IMaintainMalfunctionWorkService maintainMalfunctionWorkService;
	private final IMaintainPlanContentService maintainPlanContentService;
	private final IMaintainPlanContentExtService maintainPlanContentExtService;
	private final IMaintainPlanRelationService maintainPlanRelationService;
	private final RedisTemplate<String, Object> redisTemplate;

	/**
	 * 详情
	 */
	@GetMapping("/detail")
	@ApiOperationSupport(order = 1)
	@ApiOperation(value = "详情", notes = "传入planId")
	public R<MalFunctionDetailVO> detail(Long planId) {
		return R.data(maintainMalfunctionWorkService.getMalfunctionWorkDetail(planId));
	}

	/**
	 * 分页 故障工单明细
	 */
	@GetMapping("/list")
	@ApiOperationSupport(order = 2)
	@ApiOperation(value = "分页", notes = "传入maintainMalfunctionWork")
	public R<IPage<MaintainMalfunctionWorkVO>> list(MaintainMalfunctionWork maintainMalfunctionWork, Query query) {
		IPage<MaintainMalfunctionWork> pages = maintainMalfunctionWorkService.page(Condition.getPage(query), Condition.getQueryWrapper(maintainMalfunctionWork));
		return R.data(MaintainMalfunctionWorkWrapper.build().pageVO(pages));
	}


	/**
	 * 自定义分页 故障工单明细
	 */
	@GetMapping("/page")
	@ApiOperationSupport(order = 3)
	@ApiOperation(value = "分页", notes = "传入maintainMalfunctionWork")
	public R<IPage<MaintainMalfunctionWorkVO>> page(MaintainMalfunctionWorkVO maintainMalfunctionWork, Query query) {
		IPage<MaintainMalfunctionWorkVO> pages = maintainMalfunctionWorkService.selectMaintainMalfunctionWorkPage(Condition.getPage(query), maintainMalfunctionWork);
		return R.data(pages);
	}

	/**
	 * 新增 故障工单明细
	 */
	@PostMapping("/save")
	@ApiOperationSupport(order = 4)
	@ApiOperation(value = "新增", notes = "传入maintainMalfunctionWork")
	public R save(@Valid @RequestBody MaintainMalfunctionWork maintainMalfunctionWork) {
		return R.status(maintainMalfunctionWorkService.save(maintainMalfunctionWork));
	}

	/**
	 * 修改 故障工单明细
	 */
	@PostMapping("/update")
	@ApiOperationSupport(order = 5)
	@ApiOperation(value = "修改", notes = "传入maintainMalfunctionWork")
	public R update(@Valid @RequestBody MaintainMalfunctionWork maintainMalfunctionWork) {
		return R.status(maintainMalfunctionWorkService.updateById(maintainMalfunctionWork));
	}

	/**
	 * 新增或修改 故障工单明细
	 */
	@PostMapping("/submit")
	@ApiOperationSupport(order = 6)
	@ApiOperation(value = "新增或修改", notes = "传入maintainMalfunctionWork")
	public R submit(@Valid @RequestBody MaintainMalfunctionWork maintainMalfunctionWork) {
		return R.status(maintainMalfunctionWorkService.saveOrUpdate(maintainMalfunctionWork));
	}


	/**
	 * 删除 故障工单明细
	 */
	@GetMapping("/remove")
	@ApiOperationSupport(order = 7)
	@ApiOperation(value = "逻辑删除", notes = "传入ids")
	public R remove(@ApiParam(value = "主键集合", required = true) @RequestParam Long planId) {
		MaintainPlanContent maintainPlanContent = maintainPlanContentService.getById(planId);
		if (maintainPlanContent.getStatus().equals(TaskStatusEnum.WX_ALLOCATION.getStatus())) {
			maintainPlanContentService.removeById(planId);
			maintainMalfunctionWorkService.remove(Wrappers.<MaintainMalfunctionWork>lambdaQuery().eq(MaintainMalfunctionWork::getPlanId, planId));
		} else {
			throw new ServiceException("非待分配状态不可删除");
		}
		return R.data(true);
	}

	/**
	 * 获取报障故障现象
	 *
	 * @return
	 */
	@GetMapping("/problem/item")
	public R<?> problemItem() {
		List<Dict> problemVal = DictCache.getList("fault_problem");
		return R.data(problemVal);
	}

	/**
	 * 获取维修工单状态
	 *
	 * @return
	 */
	@GetMapping("/plan/status")
	public R<List<TaskStatusVO>> selectMalPlanStatus() {
		return R.data(TaskStatusEnum.getTaskStatusByType(MaintainPlanContentEnum.MAINTAIN_WX.getId()));
	}

	/**
	 * 获取汇总明细
	 *
	 * @return
	 */
	@GetMapping("/getSummaryDetail")
	@ApiOperation(value = "获取维修统计信息", notes = "")
	public R<MalfunctionWorkSummaryVo> getSummaryDetail() {

		return R.data(maintainMalfunctionWorkService.getSummaryDetail());
	}


	@GetMapping("/getElevatorFaultList")
	@ApiOperation(value = "获取维修电梯的故障类型统计", notes = "")
	public R<List<ProblemItemVo>> getElevatorFaultList(Long elevatorId) {
		List<ProblemItemVo> faultList = maintainMalfunctionWorkService.getElevatorFaultList(elevatorId);
		return R.data(faultList);
	}


	@GetMapping("/getMalfunctionWorkOfElevator")
	@ApiOperation(value = "根据电梯统计工单状态", notes = "")
	public R<MalfunctionWorkSummaryVo> getMalfunctionWorkElevatorInfo(Long elevatorId) {
		MalfunctionWorkSummaryVo malfunctionWorkSummaryVo = maintainMalfunctionWorkService.getMalfunctionWorkElevatorInfo(elevatorId);
		return R.data(malfunctionWorkSummaryVo);
	}


	@GetMapping("/addMalfunctionWorkUser")
	@ApiOperation(value = "增派人员", notes = "")
	public R<Boolean> addMalfunctionWorkUser(Long planId, String userIds) {
		MaintainPlanContent planContent = maintainPlanContentService.getById(planId);
		if (planContent == null) {
			throw new ServiceException("未找到相关任务工单");
		}
		if (planContent.getStatus() == TaskStatusEnum.WX_SIGN_FINISH.getStatus()) {
			throw new ServiceException("工单状态已变更请刷新页面");
		}
		return R.data(maintainMalfunctionWorkService.addMalfunctionWorkUser(planId, userIds));
	}


	@GetMapping("/giveOtherUser")
	@ApiOperation(value = "转派人员", notes = "")
	public R<Boolean> giveOtherUser(Long planId, String userIds) {
		return R.data(maintainMalfunctionWorkService.giveOtherUser(planId, userIds));
	}


	/**
	 * 获取维修部件
	 *
	 * @return
	 */
	@TenantIgnore
	@GetMapping("/parts/tree")
	public R<List<MalfunctionPartsTreeVO>> selectMalPartsTree(String keyWord) {
		return R.data(maintainMalfunctionWorkService.selectMalPartsTree(keyWord));
	}

	@GetMapping("/isCheckPlan")
	@ApiOperation(value = "检验工单来源是否质检工单", notes = "")
	public R<Boolean> isCheckPlan(@RequestParam Long planId, @RequestParam Integer type) {
		List<MaintainPlanRelation> maintainPlanRelations = maintainPlanRelationService.list(Wrappers.<MaintainPlanRelation>lambdaQuery().eq(MaintainPlanRelation::getOwnPlanId, planId));
		if (Func.isNotEmpty(maintainPlanRelations)) {
			List<Long> fromPlanIds = maintainPlanRelations.stream().map(MaintainPlanRelation::getFromPlanId).collect(Collectors.toList());
			List<MaintainPlanContent> maintainPlanContents = maintainPlanContentService.listByIds(fromPlanIds);
			if (maintainPlanContents.stream().anyMatch(maintainPlanContent -> maintainPlanContent.getTypeStatus() == type)) {
				return R.data(true);
			}
		}
		return R.data(false);
	}


	@PostMapping("getBiWxPlanDetailList")
	@ApiOperation(value = "bi-维修工单明细-下载", notes = "")
	public void getBiWxPlanDetailListDownExcel(HttpServletResponse response, Date beginTime, Date endTime) throws IOException {
		List<BiWxPlanDetailVo> list = maintainMalfunctionWorkService.getBiWxPlanDetailList(beginTime, endTime);
		ExcelWriter excelWriter = null;
		ServletOutputStream outputStream = response.getOutputStream();
		try {
			List<List<String>> rows = new ArrayList<>();
			List<String> title = CollUtil.newArrayList("工单编号", "省", "市", "区", "故障来源", "工单类型", "报障状态", "实时运行状态", "工单状态", "电梯编号",
				"电梯位置(梯号)", "所属维保组", "所属责任人", "工单创建时间", "调度时间", "第1次签到时间", "最后1次签退时间", "工单调度人员",
				"预计路程距离(KM)", "预计到达时长(分)", "实际到达耗时(分)", "工单实际执行人", "工单实际执行人数", "工单完成时间", "作业工时(小时)", "作业耗时(小时)", "完成工时(小时)",
				"项目ID", "项目名称", "项目类型", "项目要求到达时长"
			);
			rows.add(title);
			for (BiWxPlanDetailVo biWxPlanDetailVo : list) {
				List<String> row = new ArrayList<>();
				//工单编号
				row.add(Convert.toStr(biWxPlanDetailVo.getPlanId(), "/"));
				//省
				row.add(Convert.toStr(biWxPlanDetailVo.getProvinceName(), "/"));
				//市
				row.add(Convert.toStr(biWxPlanDetailVo.getCityName(), "/"));
				//区
				row.add(Convert.toStr(biWxPlanDetailVo.getAdName(), "/"));
				//故障来源
				Integer planFrom = Convert.toInt(biWxPlanDetailVo.getPlanFrom(), -1);
				if (planFrom != null) {
					//任务来源: 1: 物业端, 2: 微信, 3: 服务电话, 4: 系统派单, 5: 维保端，6: 游客, 7: web, 8: 梯联网
					if (planFrom == 1) {
						row.add("物业端");
					} else if (planFrom == 2) {
						row.add("微信");
					} else if (planFrom == 3) {
						row.add("服务电话");
					} else if (planFrom == 4) {
						row.add("系统派单");
					} else if (planFrom == 5) {
						row.add("维保端");
					} else if (planFrom == 6) {
						row.add("游客");
					} else if (planFrom == 7) {
						row.add("web");
					} else if (planFrom == 8) {
						row.add("梯联网");
					} else {
						row.add("/");
					}
				}
				//工单类型
				row.add(Convert.toStr("维修"));
				//报障状态
				row.add(Convert.toInt(biWxPlanDetailVo.getElevatorStop(), -1) == 1 ? "正常" : "停梯");
				//实时运行状态
				row.add(Convert.toInt(biWxPlanDetailVo.getNowElevatorStop(), -1) == 1 ? "正常" : "停梯");
				//工单状态
				row.add(Convert.toStr(TaskStatusEnum.getTaskName(2, biWxPlanDetailVo.getStatus())));
				//电梯编号
				row.add(Convert.toStr(biWxPlanDetailVo.getPrivateNum(), "/"));
				//电梯位置(梯号)
				row.add(Convert.toStr(biWxPlanDetailVo.getElevatorAddress(), "/"));
				//所属维保组
				row.add(Convert.toStr(biWxPlanDetailVo.getPrincipalDeptNames(), "/"));
				//所属责任人
				row.add(Convert.toStr(biWxPlanDetailVo.getPrincipalUserNames(), "/"));
				//工单创建时间
				row.add(DateUtil.format(biWxPlanDetailVo.getPlanCreateTime(), "yyyy-MM-dd HH:mm:ss"));
				//调度时间
				row.add(DateUtil.format(biWxPlanDetailVo.getAllocateTime(), "yyyy-MM-dd HH:mm:ss"));
				//第1次签到时间
				row.add(DateUtil.format(biWxPlanDetailVo.getFirstSignTime(), "yyyy-MM-dd HH:mm:ss"));
				//最后1次签退时间
				row.add(DateUtil.format(biWxPlanDetailVo.getEndSignOutTime(), "yyyy-MM-dd HH:mm:ss"));
				//工单调度人员
				if (biWxPlanDetailVo.getStatus() != 4) {
					row.add(Convert.toStr(biWxPlanDetailVo.getRealPlanUserNames()));
				} else {
					row.add(Convert.toStr(biWxPlanDetailVo.getPlanUserNames()));
				}

				//预计路程距离(KM)---暂时没有
				row.add(Convert.toStr("/"));

				//预计到达时长(分)
				row.add(Convert.toStr(biWxPlanDetailVo.getPlanArriveMinute(), "/"));

				//实际到达耗时(分)
				row.add(Convert.toStr(biWxPlanDetailVo.getRealArriveHour(), "/"));

				//工单实际执行人--如果工单状态是完成----//工单实际执行人数
				if (biWxPlanDetailVo.getStatus() == 4) {
					row.add(Convert.toStr(biWxPlanDetailVo.getRealPlanUserNames()));
					row.add(Convert.toStr((biWxPlanDetailVo.getRealPlanUserNames().split(",").length)));
				} else {
					row.add("/");
					row.add("/");
				}
				//工单完成时间
				if (biWxPlanDetailVo.getFinishPlanTime() == null) {
					row.add("/");
				} else {
					row.add(DateUtil.format(biWxPlanDetailVo.getFinishPlanTime(), "yyyy-MM-dd HH:mm:ss"));
				}
				//作业工时(小时)
				row.add(Convert.toStr(biWxPlanDetailVo.getWorkWHour(), "/"));
				//作业耗时(小时)
				row.add(Convert.toStr(biWxPlanDetailVo.getWorkHHour(), "/"));
				//完成工时(小时)
				row.add(Convert.toStr(biWxPlanDetailVo.getWorkAllHour(), "/"));
				//项目ID
				row.add(Convert.toStr(biWxPlanDetailVo.getBuildingId()));
				//项目名称
				row.add(Convert.toStr(biWxPlanDetailVo.getBuildingName()));
				//项目类型
				Integer type = Convert.toInt(biWxPlanDetailVo.getBuildingType(), -1);
				if (type != null) {
					if (type == 1) {
						row.add("小区");
					} else if (type == 2) {
						row.add("别墅");
					} else if (type == 3) {
						row.add("商超");
					} else if (type == 4) {
						row.add("酒店/民宿");
					} else if (type == 5) {
						row.add("学校");
					} else if (type == 6) {
						row.add("医院");
					} else if (type == 7) {
						row.add("工厂");
					} else if (type == 8) {
						row.add("其他");
					} else if (type == 9) {
						row.add("办公楼");
					} else if (type == 10) {
						row.add("政府单位");
					} else if (type == 11) {
						row.add("公共设施");
					} else if (type == 12) {
						row.add("出租房");
					} else {
						row.add("/");
					}
				}
				//项目要求到达时长
				row.add(Convert.toStr(biWxPlanDetailVo.getMalfunctionWorkArrivalTime(), "/"));
				rows.add(row);
			}
			String fileName = DateUtil.format(new Date(), "yyyyMMddHHmmss") + ".xlsx";
			setResponseHeader(response, fileName);
			excelWriter = ExcelUtil.getBigWriter(rows.size());
			excelWriter.write(rows, true);
			excelWriter.flush(outputStream, true);
		} catch (
			Exception e) {
			e.printStackTrace();
		} finally {
			if (excelWriter != null) {
				excelWriter.close();
			}
			outputStream.close();
		}
	}


	@PostMapping("getBiWxUserPlanList")
	@ApiOperation(value = "bi-人员工时明细-下载", notes = "")
	public void getBiWxUserPlanListDownExcel(HttpServletResponse response, Date beginTime, Date endTime) throws IOException {
		ExcelWriter excelWriter = null;
		ServletOutputStream outputStream = response.getOutputStream();
		List<BiWxUserPlanVo> biWxUserPlanDetail = maintainMalfunctionWorkService.getBiWxUserPlanDetail(beginTime, endTime);
		try {
			List<List<String>> rows = new ArrayList<>();
			List<String> title = CollUtil.newArrayList("人员ID", "省", "市", "区", "人员姓名", "人员类型", "工单ID", "工单状态", "电梯编号", "电梯位置(梯号)", "所属维保组", "所属责任人"
				, "实际执行人", "项目ID", "项目名称", "工单创建时间", "调度时间", "人员第1次签到时间", "人员最后1次签退时间", "工单调度人员", "预计路程距离(KM)", "预计到达时长(分)", "实际到达工时(小时)",
				"作业总工时(小时)", "完成总工时(小时)"
			);
			rows.add(title);
			for (BiWxUserPlanVo biWxUserPlanVo : biWxUserPlanDetail) {
				List<String> row = new ArrayList<>();
				row.add(Convert.toStr(biWxUserPlanVo.getUserId()));
				row.add(Convert.toStr(biWxUserPlanVo.getProvinceName()));
				row.add(Convert.toStr(biWxUserPlanVo.getCityName()));
				row.add(Convert.toStr(biWxUserPlanVo.getAdName()));
				row.add(Convert.toStr(biWxUserPlanVo.getUserName()));
				row.add(Convert.toStr(biWxUserPlanVo.getRoleName()));
				row.add(Convert.toStr(biWxUserPlanVo.getPlanId()));
				row.add(Convert.toStr(TaskStatusEnum.getTaskName(2, biWxUserPlanVo.getStatus())));
				row.add(Convert.toStr(biWxUserPlanVo.getPrivateNum(), "/"));
				row.add(Convert.toStr(biWxUserPlanVo.getElevatorAddress(), "/"));
				row.add(Convert.toStr(biWxUserPlanVo.getPrincipalDeptNames(), "/"));
				row.add(Convert.toStr(biWxUserPlanVo.getPrincipalUserNames(), "/"));
				//实际执行人
				row.add(Convert.toStr(biWxUserPlanVo.getRealPlanUserNames(), "/"));
				row.add(Convert.toStr(biWxUserPlanVo.getBuildingId()));
				row.add(Convert.toStr(biWxUserPlanVo.getBuildingName()));
				row.add(DateUtil.format(biWxUserPlanVo.getPlanCreateTime(), "yyyy-MM-dd HH:mm:ss"));

				row.add(biWxUserPlanVo.getAllocateTime() == null ? "/" : DateUtil.format(biWxUserPlanVo.getAllocateTime(), "yyyy-MM-dd HH:mm:ss"));
				//人员第1次签到时间
				row.add(biWxUserPlanVo.getFirstSignTime() == null ? "/" : DateUtil.format(biWxUserPlanVo.getFirstSignTime(), "yyyy-MM-dd HH:mm:ss"));
				row.add(biWxUserPlanVo.getEndSignOutTime() == null ? "/" : DateUtil.format(biWxUserPlanVo.getEndSignOutTime(), "yyyy-MM-dd HH:mm:ss"));

				//工单调度人员
				row.add(Convert.toStr(biWxUserPlanVo.getPlanUserNames()));
				//预计路程距离(KM)
				row.add("/");

				//预计到达时长(分)
				row.add(Convert.toStr(biWxUserPlanVo.getPlanArriveMinute(), "/"));

				//实际到达工时(小时)
				row.add(Convert.toStr(biWxUserPlanVo.getRealArriveHour(), "/"));

				//作业总工时(小时)
				row.add(Convert.toStr(biWxUserPlanVo.getWorkWHour(), "/"));

				//完成总工时(小时)
				row.add(Convert.toStr(biWxUserPlanVo.getWorkAllHour(), "/"));
				rows.add(row);
			}
			String fileName = DateUtil.format(new Date(), "yyyyMMddHHmmss") + ".xlsx";
			setResponseHeader(response, fileName);
			excelWriter = ExcelUtil.getBigWriter(rows.size());
			excelWriter.write(rows, true);
			excelWriter.flush(outputStream, true);
		} catch (
			Exception e) {
			e.printStackTrace();
		} finally {
			if (excelWriter != null) {
				excelWriter.close();
			}
			outputStream.close();
		}
	}


	/**
	 * 功能描述: 发送响应流方法
	 */
	private void setResponseHeader(HttpServletResponse response, String fileName) {
		try {
			try {
				fileName = new String(fileName.getBytes(), "ISO8859-1");
			} catch (UnsupportedEncodingException e) {
				e.printStackTrace();
			}
			response.setContentType("application/octet-stream;charset=ISO8859-1");
			response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
			response.addHeader("Pargam", "no-cache");
			response.addHeader("Cache-Control", "no-cache");
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}
}
